using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace BlobSim
{
    class SkyBox
    {
        #region Constants

        private const float Size = 1.0f;
        private const int Segments = 1;

        #endregion

        #region Private Data

        private static readonly CubeMapFace[] CubeMapFaces = new[] {
                CubeMapFace.NegativeX, CubeMapFace.PositiveX,
                CubeMapFace.NegativeY, CubeMapFace.PositiveY,
                CubeMapFace.NegativeZ, CubeMapFace.PositiveZ};

        private Dictionary<CubeMapFace, Texture2D> _cubeTextures;

        private VertexBuffer _vertexBuffer;
        private IndexBuffer _indexBuffer;

        private readonly BasicEffect _effect;

        #endregion

        #region Constructor

        public SkyBox(GraphicsDevice device, ContentManager content, string cubeFacesFolder)
        {
            _effect = new BasicEffect(device)
                          {
                              DiffuseColor = Color.White.ToVector3(),
                              LightingEnabled = false,
                              TextureEnabled = true
                          };

            LoadTextures(content, cubeFacesFolder);
            Initialize();
        }

        #endregion

        #region Public Methods

        public void SetProjection(float fieldOfView, float aspectRatio)
        {
            _effect.Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, 0.1f, 2.0f);
        }

        public void SetOrientation(CameraOrientation orientation)
        {
            _effect.World = Utils.CalcLookAt(orientation.HorizontalAngle, orientation.VerticalAngle);
        }

        public void Draw()
        {
            _effect.GraphicsDevice.SetVertexBuffer(_vertexBuffer);
            _effect.GraphicsDevice.Indices = _indexBuffer;

            _effect.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            _effect.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
            _effect.GraphicsDevice.BlendState = BlendState.Opaque;
            _effect.GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

            foreach (var pass in _effect.CurrentTechnique.Passes)
            {
                for (int f = 0; f < 6; f++)
                {
                    if (_cubeTextures != null)
                    {
                        var cubeMapFace = CubeMapFaces[f];
                        _effect.Texture = _cubeTextures[cubeMapFace];
                    }

                    pass.Apply();

                    int faceBaseIdx = f * Segments * 2 * (Segments + 1);
                    for (int s = 0; s < Segments; s++)
                    {
                        _effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0,
                            2 * (Segments + 1),
                            faceBaseIdx + s * 2 * (Segments + 1),
                            2 * Segments);
                    }
                }
            }
        }

        #endregion

        #region Private Implementation

        private void LoadTextures(ContentManager content, string cubeFacesFolder)
        {
            Debug.Assert(content != null && !String.IsNullOrEmpty(cubeFacesFolder));

            _cubeTextures = new Dictionary<CubeMapFace,Texture2D>();
            for (int i = 0; i < 6; i++)
            {
                var cubeMapFace = CubeMapFaces[i];
                _cubeTextures[cubeMapFace] = content.Load<Texture2D>(String.Format(@"{0}\{1}", cubeFacesFolder, cubeMapFace));
            }
        }

        private void Initialize()
        {
            VertexPositionNormalTexture[] vertices;
            ushort[] indices;
            Utils.BuildCube(Size, Segments, out vertices, out indices);

            _vertexBuffer = new VertexBuffer(_effect.GraphicsDevice, typeof(VertexPositionNormalTexture), vertices.Length, BufferUsage.None);
            _vertexBuffer.SetData(vertices);
            _indexBuffer = new IndexBuffer(_effect.GraphicsDevice, typeof(ushort), indices.Length, BufferUsage.None);
            _indexBuffer.SetData(indices);
        }

        #endregion
    }
}
